package com.denghq.projectbuilder.component.aif.config;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.denghq.projectbuilder.component.aif.service.MicroServiceCaller;
import com.denghq.projectbuilder.component.aif.utils.HttpContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.denghq.projectbuilder.component.aif.Scheme;
import com.denghq.projectbuilder.component.aif.utils.RestTemplateUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @program: key-areas-security-control
 * @description: 微代理过滤器
 * @author: Liu yang
 * @create: 2018-10-24 08:39
 **/
@Slf4j
@Component
// @WebFilter(urlPatterns = "/*", filterName = "microAgentFilter")
public class MicroAgentFilter implements Filter {

	@Resource
	RestTemplate restTemplate;

	@Autowired
	private MicroServiceCaller microServiceCaller;

	@Resource
	private RequestMappingHandlerMapping requestMappingHandlerMapping;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		log.info("handler filter init");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {

		HttpServletRequest req = (HttpServletRequest) request;

		HttpServletResponse res = (HttpServletResponse) response;
		res.setCharacterEncoding("UTF-8");
		// res.setHeader("X-Frame-Options", "ALLOW-FROM");
		res.setHeader("Access-control-Allow-Origin", req.getHeader("Origin"));
		res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
		res.setHeader("Access-Control-Allow-Headers", req.getHeader("Access-Control-Request-Headers"));
		/*if (req.getRequestURI().equals("/Common/GoLogin")) {
			res.sendRedirect(registerConfig.getLoginUrl() + "?" + req.getQueryString());
			return;
		} else {
			res.setContentType(MediaType.ALL_VALUE);
		}*/

		String requestUrl = req.getRequestURI();
		// 判断请求是否属于当前应用
		boolean isLocalService = detectHandlerMethods(req);
		if (isLocalService) {
			chain.doFilter(request, response);
		} else {
			try {
				HttpMethod method = HttpMethod.resolve(req.getMethod());
				String apiReqUrl = microServiceCaller.getApiReqUrl(requestUrl, Scheme.HTTP);
				// 获取post请求体参数，转json
				String paramsJson = getPostParams(req);
				log.info(">>>>>>>>>>>>>>>>>url>>>>>>" + apiReqUrl);

				log.info("method>>>>>>>>>>>>>>>>>" + method);
				String queryString = req.getQueryString();
				// 判断是否为文件上传请求
				if (isMultipartContent(req)) {
					doMultipartReq(apiReqUrl + "?" + queryString,req,res);
				} else if (HttpMethod.GET == method) {
					ResponseEntity<byte[]> redirectResponseEntity = null;
					if (StringUtils.isNotEmpty(queryString)) {
						apiReqUrl = apiReqUrl + "?" + queryString;
					}
					redirectResponseEntity = restTemplate.exchange(apiReqUrl, method, RestTemplateUtils.getHttpEntity(queryString, req,apiReqUrl), byte[].class);
					getMessage(res, redirectResponseEntity.getBody());
				} else if (HttpMethod.OPTIONS == method) {
					getMessage(res, "SUCCESS");
				} else {
					// 注册服务中心访问接口
					ResponseEntity<byte[]> redirectResponseEntity = restTemplate.exchange(apiReqUrl + "?" + queryString,
							method, RestTemplateUtils.getHttpEntity(paramsJson, req,apiReqUrl), byte[].class);
					// TODO:后面添加缓存机制。 暂时只过滤了post请求的。
					getMessage(res, redirectResponseEntity.getBody());
				}

			} catch (Exception e) {
				e.printStackTrace();
				Map<String, Object> data = new HashMap<String, Object>();
				data.put("code", "10029");
				data.put("codeRemark", "接口调用异常");
				data.put("message", e.getMessage());
				getMessage(res, data);
			}
		}
	}

	private void doMultipartReq(String url,HttpServletRequest req,HttpServletResponse res) throws IOException {
		
		HttpPost httpPost = new HttpPost(url);
		httpPost.addHeader("Authorization", req.getHeader("Authorization"));
		HttpClient httpClient = HttpClients.createDefault();
		MultipartEntityBuilder builder = MultipartEntityBuilder.create();
		
		MultipartResolver resolver = new StandardServletMultipartResolver();
		MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(req);
		Map<String, MultipartFile> files = multipartRequest.getFileMap();
		Iterator<String> iterator = files.keySet().iterator();
		builder.setMode(HttpMultipartMode.RFC6532);
		while (iterator.hasNext()) {
			String formKey = (String) iterator.next();
			MultipartFile multipartFile = multipartRequest.getFile(formKey);
			String filename = multipartFile.getOriginalFilename();
			// 判断是否为限制文件类型
			
			builder.addBinaryBody(formKey, multipartFile.getInputStream(),ContentType.parse(req.getContentType()),filename);// 文件流
			// builder.addTextBody("filename", filename);//
			// 类似浏览器表单提交，对应input的name和value
		}

		org.apache.http.HttpEntity entity = builder.build();
		httpPost.setEntity(entity);
		HttpResponse response = httpClient.execute(httpPost);// 执行提交

		org.apache.http.HttpEntity responseEntity = response.getEntity();
		if (responseEntity != null) {
			// 将响应内容转换为字符串
			InputStream content = responseEntity.getContent();
			BufferedInputStream bis = new BufferedInputStream(content);
			ByteArrayOutputStream buf = new ByteArrayOutputStream();
			int result = bis.read();
			while (result != -1) {
				buf.write((byte) result);
				result = bis.read();
			}
			String str = buf.toString();
			getMessage(res, str);
		}
	}

	@Override
	public void destroy() {
		log.info("handler filter destroy");
	}

	private String getPostParams(HttpServletRequest req) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(req.getInputStream(),"utf-8"));
		String str = "";
		String paramsStr = "";
		// 一行一行的读取body体里面的内容
		while ((str = reader.readLine()) != null) {
			paramsStr += str;
		}
		return paramsStr;
	}

	/**
	 * 返回消息
	 *
	 * @param res
	 * @throws IOException
	 */
	private void getMessage(HttpServletResponse res, Object data) throws IOException {
		if (data != null) {
			res.getOutputStream().write(data.toString().getBytes("utf-8"));
		}
	}
	
	/**
	 * 返回消息
	 *
	 * @param res
	 * @throws IOException
	 */
	private void getMessage(HttpServletResponse res, byte[] data) throws IOException {
		if (data != null) {
			res.getOutputStream().write(data);
		}
	}

	/**
	 * 判断当前请求url是否属于本应用
	 *
	 * @param request
	 * @return
	 */
	private boolean detectHandlerMethods(HttpServletRequest request) {

		Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
		Set<RequestMappingInfo> mappings = map.keySet();
		// Map<String, String> reversedMap = new HashMap<String, String>();
		log.info("RequestURI>>>>>>>>>>>>>>>>" + request.getRequestURI());
		String servletPath = request.getServletPath();
		if (HttpContextUtils.isStaticFile(servletPath) || servletPath.equals("/") || servletPath.indexOf("docs") != -1
				|| servletPath.indexOf("static") != -1 || servletPath.indexOf("css") != -1
				|| servletPath.indexOf("png") != -1 || servletPath.indexOf("js") != -1|| servletPath.indexOf(".ico") != -1
				|| servletPath.indexOf(".html") != -1 || servletPath.indexOf("/actuator/health") != -1
				|| servletPath.indexOf("/logs") != -1) {
			return true;
		}

		for (RequestMappingInfo info : mappings) {
			HandlerMethod method = map.get(info);
			String methodstr = method.toString();
			methodstr = methodstr.split("\\(")[0];
			methodstr = methodstr.split(" ")[2];
			int i = methodstr.lastIndexOf(".");
			methodstr = methodstr.substring(0, i);
			String urlparm = info.getPatternsCondition().toString();
			String url = urlparm.substring(1, urlparm.length() - 1);
			if (url.equals(servletPath)) {
				return true;
			} else {
				continue;
			}

		}
		return false;
	}

	/**
	 * 判断是否是multipart/form-data请求
	 * 
	 * @param request
	 * @return
	 */
	public static boolean isMultipartContent(HttpServletRequest request) {
		if (!"post".equals(request.getMethod().toLowerCase())) {
			return false;
		}

		String contentType = request.getContentType(); // 获取Content-Type
		if ((contentType != null) && (contentType.toLowerCase().startsWith("multipart/"))) {
			return true;
		} else {
			return false;
		}
	}
	
	
}
